/*
 * =====================================================================================
 *
 *       Filename:  EntityConverter.hpp
 *
 *    Description:  Convert reflex entities to xonotic entities
 *                  - Simple; operates on single entity at a time
 *                  - Only context provided is information on what entities are related.
 *                    (i.e. a teleport and it's destination)  Can get this information
 *                    through the pre-scan constructor of the .map file or by providing
 *                    a queue of all the entities in the file.
 *                  - Throws exceptions upon encountering malformed entities and when 
 *                    IO errors occur during object instantiation
 *
 *        Version:  1.0
 *        Created:  05/27/2017 08:21:14 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  suhrke@protonmail.com
 *
 * =====================================================================================
 */

#ifndef ENTITY_CONVERTER_HPP
#define ENTITY_CONVERTER_HPP


#include <map>
#include <string>
#include <queue>
#include <vector>







struct WorldSpawn
{
  bool cts;
  bool ctf;
  bool ffa;
  bool tdm;
  bool duel;
};



class EntityConverter
{
  public:
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  Constructor
     * Description:  Creates entity format mapping
     *     CAUTION:  Requires extractMapInfo method to be called after this
     *    Requires:  Reflex Entity Mapping filename
     *      THROWS:  runtime_error on Reflex Entity Mapping file format error
     *      THROWS:  std::ios::failure on IO failure
     *--------------------------------------------------------------------------------------
     */
    EntityConverter (const std::string &entityMapFile);
    /* *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  Constructor
     * Description:  Creates entity format mapping  and pre-scans for map info
     *   Parameter:  string entityMapFile, file maps source to target entity formats
     *   Parameter:  string reflexMapFile, for pre-scan
     *      THROWS:  runtime_error on Reflex Entity Mapping file format error
     *      THROWS:  std::ios::failure on IO failure
     *--------------------------------------------------------------------------------------
     */
    EntityConverter (const std::string &entityMapFile, const std::string &reflexMapFile);
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: convert
     * Description:  Converts a single entity from reflex to xonotic format
     *   Parameter:  vector of strings lines, lines that comprise a single entity
     *      Return:  vector of strings, single entity in the converted format
     *               *IF entity is not supported, returns EMPTY vector
     *      THROWS:  runtime_error on malformed .map file
     *      THROWS:  runtime_error when called before map info has been extracted
     *--------------------------------------------------------------------------------------
     */
    std::vector<std::string> convert (const std::vector<std::string> &lines);
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: extractMapInfo
     * Description:  Get information needed by the converter that can't be obtained
     *               in entity-by-entity conversion (teleport and jump pad
     *   Parameter:  queue of vector of string entities, ALL entities in a .map file
     *      THROWS:  runtime_error when encountering malformed entity
     *--------------------------------------------------------------------------------------
     */
    void extractMapInfo (std::queue<std::vector<std::string>> entities);
     /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: extractMapInfo
     * Description:  Get information needed by the converter that can't be obtained
     *               in entity-by-entity conversion (teleport and jump pad
     *   Parameter:  vector of vector of string entities, ALL entities in a .map file
     *      THROWS:  runtime_error when encountering malformed entity
     *--------------------------------------------------------------------------------------
     */
    void extractMapInfo (const std::vector<std::vector<std::string>> &entities);
    


  protected:
    

    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: convert~EntityName~
     * Description:  Multiple methods to convert entity from reflex to xonotic format
     *   Parameter:  vector of strings lines, multi-lined entity
     *      Return:  vector of strings, the converted entity
     *--------------------------------------------------------------------------------------
     */
    std::vector<std::string> convertPickup (const std::vector<std::string> &lines) const;
    std::vector<std::string> convertPlayerSpawn (const std::vector<std::string> &lines) const;
    std::vector<std::string> convertJumpPad (const std::vector<std::string> &lines) const;
    std::vector<std::string> convertTeleporter (const std::vector<std::string> &lines) const;
    std::vector<std::string> convertTarget (const std::vector<std::string> &lines) const;
    std::vector<std::string> convertRaceStart (const std::vector<std::string> &lines) const;
    std::vector<std::string> convertRaceFinish (const std::vector<std::string> &lines) const;
    std::vector<std::string> convertPointLight (const std::vector<std::string> &lines) const;

    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: getAttributeType
     * Description:  Extracts the type from a line
     *   Parameter: string "line", entity keyword followed by the type
     *--------------------------------------------------------------------------------------
     */
    std::string getAttributeType (const std::string &line) const;
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: mapEntities
     * Description:  Prepare pickupMap
     *   Parameter:  string mapFile, filename of pickup mapping
     *      Return:  true if no error, false if error
     *--------------------------------------------------------------------------------------
     */
    void mapEntities (const std::string &mapFile);
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: haveRequiredMappings
     * Description:  Check that required mappings exist
     *--------------------------------------------------------------------------------------
     */
    bool haveRequiredMappings();
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: extractFromEntity
     * Description:  Get map info from a single entity
     *   Paramater:  string line, the previous line (contains entity type)
     *   Parameter:  istream is, an ifstream or a stringstream containing a 
     *               single entity
     *--------------------------------------------------------------------------------------
     */
    void extractFromEntity (const std::string &line, std::istream &is);
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: offset
     * Description:  Reflex coordinates place entities at the ground and Xonotic entities
     *               are at about center of player height.  Offset accordingly.
     *   Paramater:  string value, float value passed as string
     *   Parameter:  float offset, amount to add to value
     *      Return:  string, float value passed as string
     *--------------------------------------------------------------------------------------
     */
    std::string offset (const std::string &value, const float offset) const;
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: adjustAngleForHandedness
     * Description:  Axis swaps require angles to take this into account
     *   Parameter:  string angle, an angle in degrees
     *      Return:  string, the adjusted angle in degrees
     *--------------------------------------------------------------------------------------
     */
    std::string adjustAngleForHandedness (const std::string &angle) const;
    /*
     *--------------------------------------------------------------------------------------
     *       Class:  EntityConverter
     *      Method:  EntityConverter :: hexToRGB
     * Description:  Convert 8 digit hex value into separate red, green, and blue values
     *   Parameter:  string hex, inputted hex RGBA value (leftmost byte is alpha, then RGB)
     *   Parameter:  float r, RETURN BY REFERENCE: converted red value
     *   Parameter:  float g, RETURN BY REFERENCE: converted green value
     *   Parameter:  float b, RETURN BY REFERENCE: converted blue value
     *--------------------------------------------------------------------------------------
     */
     void hexToRGB (const std::string &hex, float &r, float &g, float &b) const;
     /*
      *--------------------------------------------------------------------------------------
      *       Class:  EntityConverter
      *      Method:  EntityConverter :: adjustBrightness
      * Description:  Reflex uses significantly smaller values than Xonotic -> adjust
      *   Parameter:  string value, original brightness value
      *--------------------------------------------------------------------------------------
      */
      int adjustBrightness (const std::string &value) const;
      /*
       *--------------------------------------------------------------------------------------
       *       Class:  EntityConverter
       *      Method:  EntityConverter :: makeErrorMessage
       * Description:  Combine a message and the entity responsible into an error message
       *--------------------------------------------------------------------------------------
       */
      std::string makeErrorMessage (const std::string message, 
                                    const std::vector<std::string> entity) const;



    // Map Reflex pickup IDs to Xonotic pickup identifiers
    std::map<std::string, std::string> entityMap_;
    // Map targets (by name) to their source type
    std::map<std::string, std::string> targetMap_;
    // Related entities must be matched prior to entity conversion
    bool haveMapInfo_;
    WorldSpawn ws_;

    // Offsets for item/spawn height
    const float OFFSET_PLAYER;
    const float OFFSET_PICKUP;
    // Brightness adjustment factor
    const float BRIGHTNESS_ADJUST;
    // Floating point precision for output
    const int OUTPUT_PRECISION;



  private:
    void printMapping() const; //DEBUG
    void printTargetSources() const; //DEBUG

};

#endif //ENTITY_CONVERTER_HPP

